home *** CD-ROM | disk | FTP | other *** search
/ The World's Largest Collection of Windows Software / The World's Largest Collection of Windows Software - Disc 2.iso / textproc / _j1 / tex2rtf / src / texutils.cc < prev    next >
C/C++ Source or Header  |  1993-10-25  |  22KB  |  932 lines

  1. /*
  2.  * Miscellaneous utilities for manipulating LaTeX files and constructs
  3.  *
  4.  */
  5.  
  6. #include <wx.h>
  7. #include <iostream.h>
  8. #include <ctype.h>
  9. #include "tex2any.h"
  10.  
  11. wxList TexReferences(wxKEY_STRING);
  12. wxList BibList(wxKEY_STRING);
  13. wxStringList CitationList;
  14. wxList CustomMacroList(wxKEY_STRING);
  15.  
  16. // Look for \label macro, use this ref name if found or
  17. // make up a topic name otherwise.
  18. static long topicCounter = 0;
  19.  
  20. void ResetTopicCounter(void)
  21. {
  22.   topicCounter = 0;
  23. }
  24.  
  25. char *FindTopicName(TexChunk *chunk)
  26. {
  27.   char *topicName = NULL;
  28.   static char topicBuf[100];
  29.  
  30.   if (chunk && (chunk->type == CHUNK_TYPE_MACRO) &&
  31.       (strcmp(chunk->name, "label") == 0))
  32.   {
  33.     wxNode *node = chunk->children.First();
  34.     if (node)
  35.     {
  36.       TexChunk *child = (TexChunk *)node->Data();
  37.       if (child->type == CHUNK_TYPE_ARG)
  38.       {
  39.         wxNode *snode = child->children.First();
  40.         if (snode)
  41.         {
  42.           TexChunk *schunk = (TexChunk *)snode->Data();
  43.           if (schunk->type == CHUNK_TYPE_STRING)
  44.             topicName = schunk->value;
  45.         }
  46.       }
  47.     }
  48.   }
  49.   if (topicName)
  50.     return topicName;
  51.   else
  52.   {
  53.     sprintf(topicBuf, "topic%ld", topicCounter);
  54.     topicCounter ++;
  55.     return topicBuf;
  56.   }
  57. }
  58.  
  59. /*
  60.  * Simulate argument data, so we can 'drive' clients which implement
  61.  * certain basic formatting behaviour.
  62.  * Snag is that some save a TexChunk, so don't use yet...
  63.  *
  64.  */
  65.  
  66. void StartSimulateArgument(char *data)
  67. {
  68.   strcpy(currentArgData, data);
  69.   haveArgData = TRUE;
  70. }
  71.  
  72. void EndSimulateArgument(void)
  73. {
  74.   haveArgData = FALSE;
  75. }
  76.  
  77. /*
  78.  * Parse and convert unit arguments to points
  79.  *
  80.  */
  81.  
  82. int ParseUnitArgument(char *unitArg)
  83. {
  84.   float conversionFactor = 1.0;
  85.   float unitValue = 0.0;
  86.   int len = strlen(unitArg);
  87.   if (unitArg && (len > 0) && (isdigit(unitArg[0]) || unitArg[0] == '-'))
  88.   {
  89.     sscanf(unitArg, "%f", &unitValue);
  90.     if (len > 1)
  91.     {
  92.       char units[3]; 
  93.       units[0] = unitArg[len-1];
  94.       units[1] = unitArg[len-2];
  95.       units[2] = 0;
  96.       if (strcmp(units, "in") == 0)
  97.         conversionFactor = 72.0;
  98.       else if (strcmp(units, "cm") == 0)
  99.         conversionFactor = 72.0/2.51;
  100.       else if (strcmp(units, "mm") == 0)
  101.         conversionFactor = 72.0/25.1;
  102.       else if (strcmp(units, "pt") == 0)
  103.         conversionFactor = 1;
  104.     }
  105.     return (int)(unitValue*conversionFactor);
  106.   }
  107.   else return 0;
  108. }
  109.  
  110. /*
  111.  * Strip off any extension (dot something) from end of file,
  112.  * IF one exists. Inserts zero into buffer.
  113.  *
  114.  */
  115.  
  116. void StripExtension(char *buffer)
  117. {
  118.   int len = strlen(buffer);
  119.   int i = len-1;
  120.   while (i > 0)
  121.   {
  122.     if (buffer[i] == '.')
  123.     {
  124.       buffer[i] = 0;
  125.       break;
  126.     }
  127.     i --;
  128.   }
  129. }
  130.  
  131. /*
  132.  * Latex font setting
  133.  *
  134.  */
  135.  
  136. void SetFontSizes(int pointSize)
  137. {
  138.   switch (pointSize)
  139.   {
  140.     case 12:
  141.     {
  142.       normalFont = 12;
  143.       smallFont = 10;
  144.       tinyFont = 8;
  145.       largeFont1 = 14;
  146.       LargeFont2 = 16;
  147.       LARGEFont3 = 20;
  148.       hugeFont1 = 24;
  149.       HugeFont2 = 28;
  150.       HUGEFont3 = 32;
  151.       break;
  152.     }
  153.     case 11:
  154.     {
  155.       normalFont = 11;
  156.       smallFont = 9;
  157.       tinyFont = 7;
  158.       largeFont1 = 13;
  159.       LargeFont2 = 16;
  160.       LARGEFont3 = 19;
  161.       hugeFont1 = 22;
  162.       HugeFont2 = 26;
  163.       HUGEFont3 = 30;
  164.       break;
  165.     }
  166.     case 10:
  167.     {
  168.       normalFont = 10;
  169.       smallFont = 8;
  170.       tinyFont = 6;
  171.       largeFont1 = 12;
  172.       LargeFont2 = 14;
  173.       LARGEFont3 = 18;
  174.       hugeFont1 = 20;
  175.       HugeFont2 = 24;
  176.       HUGEFont3 = 28;
  177.       break;
  178.     }
  179.   }
  180. }
  181.  
  182.  
  183. /*
  184.  * Latex references
  185.  *
  186.  */
  187.  
  188. void AddTexRef(char *name, char *file, char *sectionName,
  189.                int chapter, int section, int subsection, int subsubsection)
  190. {
  191.   wxNode *node = TexReferences.Find(name);
  192.   if (node) delete node;
  193.   char buf[100];
  194.   buf[0] = 0;
  195.   if (sectionName)
  196.   {
  197.     strcat(buf, sectionName);
  198.     strcat(buf, " ");
  199.   }
  200.  
  201.   if (chapter)
  202.   {
  203.     char buf2[10];
  204.     sprintf(buf2, "%d", chapter);
  205.     strcat(buf, buf2);
  206.   }
  207.   if (section)
  208.   {
  209.     char buf2[10];
  210.     if (chapter)
  211.       strcat(buf, ".");
  212.  
  213.     sprintf(buf2, "%d", section);
  214.     strcat(buf, buf2);
  215.   }
  216.   if (subsection)
  217.   {
  218.     char buf2[10];
  219.     strcat(buf, ".");
  220.     sprintf(buf2, "%d", subsection);
  221.     strcat(buf, buf2);
  222.   }
  223.   if (subsubsection)
  224.   {
  225.     char buf2[10];
  226.     sprintf(buf2, "%d", subsubsection);
  227.     strcat(buf, buf2);
  228.   }
  229.   TexReferences.Append(name, new TexRef(name, file, (strlen(buf) > 0) ? buf : NULL));
  230. }
  231.  
  232. void WriteTexReferences(char *filename)
  233. {
  234.   ofstream ostr(filename);
  235.   if (ostr.bad()) return;
  236.   char buf[200];
  237.   
  238.   wxNode *node = TexReferences.First();
  239.   while (node)
  240.   {
  241.     TexRef *ref = (TexRef *)node->Data();
  242.     ostr << ref->refLabel << " " << (ref->refFile ? ref->refFile : "??") << " ";
  243.     ostr << (ref->sectionNumber ? ref->sectionNumber : "??") << "\n";
  244.     if (!ref->sectionNumber || (strcmp(ref->sectionNumber, "??") == 0))
  245.     {
  246.       sprintf(buf, "Warning: reference %s not resolved.", ref->refLabel);
  247.       OnInform(buf);
  248.     }
  249.     node = node->Next();
  250.   }
  251. }
  252.  
  253. void ReadTexReferences(char *filename)
  254. {
  255.   ifstream istr(filename);
  256.   if (istr.bad()) return;
  257.  
  258.   char label[100];
  259.   char file[400];
  260.   char section[100];
  261.  
  262.   while (!istr.eof())
  263.   {
  264.     istr >> label;
  265.     if (!istr.eof())
  266.     {
  267.       istr >> file;
  268.       char ch;
  269.       istr.get(ch); // Read past space
  270.       istr.get(ch);
  271.       int i = 0;
  272.       while (ch != '\n' && !istr.eof())
  273.       {
  274.         section[i] = ch;
  275.         i ++;
  276.         istr.get(ch);
  277.       }
  278.       section[i] = 0;
  279.       TexReferences.Append(label, new TexRef(label, file, section));
  280.     }
  281.   }
  282. }
  283.  
  284.  
  285. /*
  286.  * Bibliography-handling code
  287.  *
  288.  */
  289.  
  290. void BibEatWhiteSpace(istream& str)
  291. {
  292.   char ch = str.peek();
  293.   
  294.   while (!str.eof() && (ch == ' ' || ch == '\t' || ch == 13 || ch == 10 || ch == EOF))
  295.   {
  296.     str.get(ch);
  297.     ch = str.peek();
  298.   }
  299. }
  300.  
  301. // Read word up to { or , or space
  302. void BibReadWord(istream& istr, char *buffer)
  303. {
  304.   int i = 0;
  305.   buffer[i] = 0;
  306.   char ch = istr.peek();
  307.   while (!istr.eof() && ch != ' ' && ch != '{' && ch != 13 && ch != 10 && ch != '\t' &&
  308.          ch != ',' && ch != '=')
  309.   {
  310.     istr.get(ch);
  311.     buffer[i] = ch;
  312.     i ++;
  313.     ch = istr.peek();
  314.   }
  315.   buffer[i] = 0;
  316. }
  317.  
  318. void BibReadToEOL(istream& istr, char *buffer)
  319. {
  320.   int i = 0;
  321.   buffer[i] = 0;
  322.   char ch = istr.peek();
  323.   while (!istr.eof() && ch != 13 && ch != 10)
  324.   {
  325.     istr.get(ch);
  326.     buffer[i] = ch;
  327.     i ++;
  328.     ch = istr.peek();
  329.   }
  330.   buffer[i] = 0;
  331. }
  332.  
  333. // Read }-terminated value, taking nested braces into account.
  334. void BibReadValue(istream& istr, char *buffer, Bool ignoreBraces = TRUE,
  335.                   Bool quotesMayTerminate = TRUE)
  336. {
  337.   int braceCount = 1;
  338.   int i = 0;
  339.   buffer[i] = 0;
  340.   char ch = istr.peek();
  341.   Bool stopping = FALSE;
  342.   while (!istr.eof() && !stopping)
  343.   {
  344.     istr.get(ch);
  345.     
  346.     if (ch == '{')
  347.       braceCount ++;
  348.  
  349.     if (ch == '}')
  350.     {
  351.       braceCount --;
  352.       if (braceCount == 0)
  353.       {
  354.         stopping = TRUE;
  355.         break;
  356.       }
  357.     }
  358.     else if (quotesMayTerminate && ch == '"')
  359.     {
  360.       stopping = TRUE;
  361.       break;
  362.     }
  363.     if (!stopping)
  364.     {
  365.       if (!ignoreBraces || (ch != '{' && ch != '}'))
  366.       {
  367.         buffer[i] = ch;
  368.         i ++;
  369.       }
  370.     }
  371.   }
  372.   buffer[i] = 0;
  373. }
  374.  
  375. Bool ReadBib(char *filename)
  376. {
  377.   ifstream istr(filename);
  378.   if (istr.bad()) return FALSE;
  379.  
  380.   OnInform("Reading .bib file...");
  381.  
  382.   char ch;
  383.   char fieldValue[2000];
  384.   char recordType[100];
  385.   char recordKey[100];
  386.   char recordField[100];
  387.   while (!istr.eof())
  388.   {
  389.     BibEatWhiteSpace(istr);
  390.     istr.get(ch);
  391.     if (ch != '@')
  392.     {
  393.       OnError("Expected @: malformed .bib file.");
  394.       return FALSE;
  395.     }
  396.     BibReadWord(istr, recordType);
  397.     BibEatWhiteSpace(istr);
  398.     istr.get(ch);
  399.     if (ch != '{' && ch != '(')
  400.     {
  401.       OnError("Expected { or ( after record type: malformed .bib file.");
  402.       return FALSE;
  403.     }
  404.     BibEatWhiteSpace(istr);
  405.     if (StringMatch(recordType, "string", FALSE, TRUE))
  406.     {
  407.       BibReadWord(istr, fieldValue);
  408.       BibEatWhiteSpace(istr);
  409.       istr.get(ch);
  410.       if (ch != '=')
  411.       {
  412.         OnError("Expected = after string key: malformed .bib file.");
  413.         return FALSE;
  414.       }
  415.       BibEatWhiteSpace(istr);
  416.       istr.get(ch);
  417.       if (ch != '"' && ch != '{')
  418.       {
  419.         OnError("Expected = after string key: malformed .bib file.");
  420.         return FALSE;
  421.       }
  422.       BibReadValue(istr, fieldValue);
  423.       BibEatWhiteSpace(istr);
  424.     }
  425.     else
  426.     {
  427.       BibReadWord(istr, recordKey);
  428.  
  429.       BibEntry *bibEntry = new BibEntry;
  430.       bibEntry->key = copystring(recordKey);
  431.       bibEntry->type = copystring(recordType);
  432.  
  433.       Bool moreRecords = TRUE;
  434.       while (moreRecords && !istr.eof())
  435.       {
  436.         BibEatWhiteSpace(istr);
  437.         istr.get(ch);
  438.         if (ch == '}' || ch == ')')
  439.         {
  440.           moreRecords = FALSE;
  441.         }
  442.         else if (ch == ',')
  443.         {
  444.           BibEatWhiteSpace(istr);
  445.           BibReadWord(istr, recordField);
  446.           BibEatWhiteSpace(istr);
  447.           istr.get(ch);
  448.           if (ch != '=')
  449.           {
  450.             OnError("Expected = after field type: malformed .bib file.");
  451.             return FALSE;
  452.           }
  453.           BibEatWhiteSpace(istr);
  454.           istr.get(ch);
  455.           if (ch != '{' && ch != '"')
  456.           {
  457.             fieldValue[0] = ch;
  458.             BibReadWord(istr, fieldValue+1);
  459.           }
  460.           else
  461.             BibReadValue(istr, fieldValue);
  462.  
  463.           // Now we can add a field
  464.           if (StringMatch(recordField, "author", FALSE, TRUE))
  465.             bibEntry->author = copystring(fieldValue);
  466.           else if (StringMatch(recordField, "key", FALSE, TRUE))
  467.             {}
  468.           else if (StringMatch(recordField, "annotate", FALSE, TRUE))
  469.             {}
  470.           else if (StringMatch(recordField, "edition", FALSE, TRUE))
  471.             {}
  472.           else if (StringMatch(recordField, "howpublished", FALSE, TRUE))
  473.             {}
  474.           else if (StringMatch(recordField, "note", FALSE, TRUE))
  475.             {}
  476.           else if (StringMatch(recordField, "series", FALSE, TRUE))
  477.             {}
  478.           else if (StringMatch(recordField, "type", FALSE, TRUE))
  479.             {}
  480.           else if (StringMatch(recordField, "editor", FALSE, TRUE))
  481.             bibEntry->editor= copystring(fieldValue);
  482.           else if (StringMatch(recordField, "title", FALSE, TRUE))
  483.             bibEntry->title= copystring(fieldValue);
  484.           else if (StringMatch(recordField, "booktitle", FALSE, TRUE))
  485.             bibEntry->booktitle= copystring(fieldValue);
  486.           else if (StringMatch(recordField, "journal", FALSE, TRUE))
  487.             bibEntry->journal= copystring(fieldValue);
  488.           else if (StringMatch(recordField, "volume", FALSE, TRUE))
  489.             bibEntry->volume= copystring(fieldValue);
  490.           else if (StringMatch(recordField, "number", FALSE, TRUE))
  491.             bibEntry->number= copystring(fieldValue);
  492.           else if (StringMatch(recordField, "year", FALSE, TRUE))
  493.             bibEntry->year= copystring(fieldValue);
  494.           else if (StringMatch(recordField, "month", FALSE, TRUE))
  495.             bibEntry->month= copystring(fieldValue);
  496.           else if (StringMatch(recordField, "pages", FALSE, TRUE))
  497.             bibEntry->pages= copystring(fieldValue);
  498.           else if (StringMatch(recordField, "publisher", FALSE, TRUE))
  499.             bibEntry->publisher= copystring(fieldValue);
  500.           else if (StringMatch(recordField, "address", FALSE, TRUE))
  501.             bibEntry->address= copystring(fieldValue);
  502.           else if (StringMatch(recordField, "institution", FALSE, TRUE) || StringMatch(recordField, "school", FALSE, TRUE))
  503.             bibEntry->institution= copystring(fieldValue);
  504.           else if (StringMatch(recordField, "organization", FALSE, TRUE))
  505.             bibEntry->organization= copystring(fieldValue);
  506.           else if (StringMatch(recordField, "comment", FALSE, TRUE))
  507.             bibEntry->comment= copystring(fieldValue);
  508.           else if (StringMatch(recordField, "chapter", FALSE, TRUE))
  509.             bibEntry->chapter= copystring(fieldValue);
  510.           else
  511.           {
  512.             char buf[200];
  513.             sprintf(buf, "Unrecognised field type %s", recordField);
  514.             OnError(buf);
  515.           }
  516.         }
  517.         BibList.Append(recordKey, bibEntry);
  518.         BibEatWhiteSpace(istr);
  519.       }
  520.     }
  521.   }
  522.   return TRUE;
  523. }
  524.  
  525. void OutputBibItem(TexRef *ref, BibEntry *bib)
  526. {
  527.   OnMacro("bibitem", 2, TRUE);
  528.   OnArgument("bibitem", 1, TRUE);
  529.   TexOutput(ref->sectionNumber);
  530.   OnArgument("bibitem", 1, FALSE);
  531.   OnArgument("bibitem", 2, TRUE);
  532.  
  533.   TexOutput(" ");
  534.   OnMacro("bf", 1, TRUE);
  535.   OnArgument("bf", 1, TRUE);
  536.   if (bib->author)
  537.     TexOutput(bib->author);
  538.   OnArgument("bf", 1, FALSE);
  539.   OnMacro("bf", 1, FALSE);
  540.   if (bib->author && (strlen(bib->author) > 0) && (bib->author[strlen(bib->author) - 1] != '.'))
  541.     TexOutput(". ");
  542.   else
  543.     TexOutput(" ");
  544.  
  545.   if (bib->year)
  546.   {
  547.     TexOutput(bib->year);
  548.   }
  549.   if (bib->month)
  550.   {
  551.     TexOutput(" (");
  552.     TexOutput(bib->month);
  553.     TexOutput(")");
  554.   }
  555.   if (bib->year || bib->month)
  556.     TexOutput(". ");
  557.  
  558.   if (StringMatch(bib->type, "article", FALSE, TRUE))
  559.   {
  560.     if (bib->title)
  561.     {
  562.       TexOutput(bib->title);
  563.       TexOutput(". ");
  564.     }
  565.     if (bib->journal)
  566.     {
  567.       OnMacro("it", 1, TRUE);
  568.       OnArgument("it", 1, TRUE);
  569.       TexOutput(bib->journal);
  570.       OnArgument("it", 1, FALSE);
  571.       OnMacro("it", 1, FALSE);
  572.     }
  573.     if (bib->volume)
  574.     {
  575.       TexOutput(", ");
  576.       OnMacro("bf", 1, TRUE);
  577.       OnArgument("bf", 1, TRUE);
  578.       TexOutput(bib->volume);
  579.       OnArgument("bf", 1, FALSE);
  580.       OnMacro("bf", 1, FALSE);
  581.     }
  582.     if (bib->number)
  583.     {
  584.       TexOutput("(");
  585.       TexOutput(bib->number);
  586.       TexOutput(")");
  587.     }
  588.     if (bib->pages)
  589.     {
  590.       TexOutput(", pages ");
  591.       TexOutput(bib->pages);
  592.     }
  593.     TexOutput(".");
  594.   }
  595.   else if (StringMatch(bib->type, "book", FALSE, TRUE) ||
  596.            StringMatch(bib->type, "unpublished", FALSE, TRUE) ||
  597.            StringMatch(bib->type, "manual", FALSE, TRUE) ||
  598.            StringMatch(bib->type, "phdthesis", FALSE, TRUE) ||
  599.            StringMatch(bib->type, "mastersthesis", FALSE, TRUE) ||
  600.            StringMatch(bib->type, "misc", FALSE, TRUE) ||
  601.            StringMatch(bib->type, "techreport", FALSE, TRUE) ||
  602.            StringMatch(bib->type, "booklet", FALSE, TRUE))
  603.   {
  604.     if (bib->title || bib->booktitle)
  605.     {
  606.       OnMacro("it", 1, TRUE);
  607.       OnArgument("it", 1, TRUE);
  608.       TexOutput(bib->title ? bib->title : bib->booktitle);
  609.       TexOutput(". ");
  610.       OnArgument("it", 1, FALSE);
  611.       OnMacro("it", 1, FALSE);
  612.     }
  613.     if (StringMatch(bib->type, "phdthesis", FALSE, TRUE))
  614.       TexOutput("PhD thesis. ");
  615.     if (StringMatch(bib->type, "techreport", FALSE, TRUE))
  616.       TexOutput("Technical report. ");
  617.     if (bib->editor)
  618.     {
  619.       TexOutput("Ed. ");
  620.       TexOutput(bib->editor);
  621.       TexOutput(". ");
  622.     }
  623.     if (bib->institution)
  624.     {
  625.       TexOutput(bib->institution);
  626.       TexOutput(". ");
  627.     }
  628.     if (bib->organization)
  629.     {
  630.       TexOutput(bib->organization);
  631.       TexOutput(". ");
  632.     }
  633.     if (bib->publisher)
  634.     {
  635.       TexOutput(bib->publisher);
  636.       TexOutput(". ");
  637.     }
  638.     if (bib->address)
  639.     {
  640.       TexOutput(bib->address);
  641.       TexOutput(". ");
  642.     }
  643.   }
  644.   else if (StringMatch(bib->type, "inbook", FALSE, TRUE) ||
  645.            StringMatch(bib->type, "inproceedings", FALSE, TRUE) ||
  646.            StringMatch(bib->type, "incollection", FALSE, TRUE) ||
  647.            StringMatch(bib->type, "conference", FALSE, TRUE))
  648.   {
  649.     if (bib->title)
  650.     {
  651.       TexOutput(bib->title);
  652.     }
  653.     if (bib->booktitle)
  654.     {
  655.       TexOutput(", from ");
  656.       OnMacro("it", 1, TRUE);
  657.       OnArgument("it", 1, TRUE);
  658.       TexOutput(bib->booktitle);
  659.       TexOutput(".");
  660.       OnArgument("it", 1, FALSE);
  661.       OnMacro("it", 1, FALSE);
  662.     }
  663.     if (bib->editor)
  664.     {
  665.       TexOutput(", ed. ");
  666.       TexOutput(bib->editor);
  667.     }
  668.     if (bib->publisher)
  669.     {
  670.       TexOutput(" ");
  671.       TexOutput(bib->publisher);
  672.     }
  673.     if (bib->address)
  674.     {
  675.       if (bib->publisher) TexOutput(", ");
  676.       else TexOutput(" ");
  677.       TexOutput(bib->address);
  678.     }
  679.     if (bib->publisher || bib->address)
  680.       TexOutput(".");
  681.  
  682.     if (bib->volume)
  683.     {
  684.       TexOutput(" ");
  685.       OnMacro("bf", 1, TRUE);
  686.       OnArgument("bf", 1, TRUE);
  687.       TexOutput(bib->volume);
  688.       OnArgument("bf", 1, FALSE);
  689.       OnMacro("bf", 1, FALSE);
  690.     }
  691.     if (bib->number)
  692.     {
  693.       if (bib->volume)
  694.       {
  695.         TexOutput("(");
  696.         TexOutput(bib->number);
  697.         TexOutput(").");
  698.       }
  699.       else
  700.       {
  701.         TexOutput(" Number ");
  702.         TexOutput(bib->number);
  703.         TexOutput(".");
  704.       }
  705.     }
  706.     if (bib->chapter)
  707.     {
  708.       TexOutput(" Chap. "); TexOutput(bib->chapter);
  709.     }
  710.     if (bib->pages)
  711.     {
  712.       if (bib->chapter) TexOutput(", pages ");
  713.       else TexOutput(" Pages ");
  714.       TexOutput(bib->pages);
  715.       TexOutput(".");
  716.     }
  717.   }
  718.   OnArgument("bibitem", 2, FALSE);
  719.   OnMacro("bibitem", 2, FALSE);
  720. }
  721.  
  722. void OutputBib(void)
  723. {
  724.   // Write the title
  725.   OnMacro("references", 0, TRUE);
  726.   OnMacro("references", 0, FALSE);
  727.  
  728.   wxNode *node = CitationList.First();
  729.   while (node)
  730.   {
  731.     char *citeKey = (char *)node->Data();
  732.     wxNode *texNode = TexReferences.Find(citeKey);
  733.     wxNode *bibNode = BibList.Find(citeKey);
  734.     if (bibNode && texNode)
  735.     {
  736.       BibEntry *entry = (BibEntry *)bibNode->Data();
  737.       TexRef *ref = (TexRef *)texNode->Data();
  738.       OutputBibItem(ref, entry);
  739.     }
  740.     node = node->Next();
  741.   }
  742. }
  743.  
  744. static int citeCount = 1;
  745.  
  746. void ResolveBibReferences(void)
  747. {
  748.   if (CitationList.Number() > 0)
  749.     OnInform("Resolving bibliographic references...");
  750.  
  751.   citeCount = 1;
  752.   char buf[200];
  753.   wxNode *node = CitationList.First();
  754.   while (node)
  755.   {
  756.     char *citeKey = (char *)node->Data();
  757.     wxNode *texNode = TexReferences.Find(citeKey);
  758.     wxNode *bibNode = BibList.Find(citeKey);
  759.     if (bibNode && texNode)
  760.     {
  761.       TexRef *ref = (TexRef *)texNode->Data();
  762.       BibEntry *entry = (BibEntry *)bibNode->Data();
  763.       if (ref->sectionNumber) delete[] ref->sectionNumber;
  764.       sprintf(buf, "[%d]", citeCount);
  765.       ref->sectionNumber = copystring(buf);
  766.       citeCount ++;
  767.     }
  768.     else
  769.     {
  770.       sprintf(buf, "Warning: bib ref %s not resolved.", citeKey);
  771.       OnInform(buf);
  772.     }
  773.     node = node->Next();
  774.   }
  775. }
  776.  
  777. // Remember we need to resolve this citation
  778. void AddCitation(char *citeKey)
  779. {
  780.   if (!CitationList.Member(citeKey))
  781.     CitationList.Add(citeKey);
  782.  
  783.   if (!TexReferences.Find(citeKey))
  784.   {
  785.     TexReferences.Append(citeKey, new TexRef(citeKey, "??", NULL));
  786.   }
  787. }
  788.  
  789. /*
  790.  * Custom macro stuff
  791.  *
  792.  */
  793.  
  794. // Define a variable value from the .ini file
  795. void RegisterSetting(char *settingName, char *settingValue)
  796. {
  797.   if (StringMatch(settingName, "chapterFontSize", FALSE, TRUE))
  798.     StringToInt(settingValue, &chapterFont);
  799.   else if (StringMatch(settingName, "sectionFontSize", FALSE, TRUE))
  800.     StringToInt(settingValue, §ionFont);
  801.   else if (StringMatch(settingName, "subsectionFontSize", FALSE, TRUE))
  802.     StringToInt(settingValue, &subsectionFont);
  803.   else if (StringMatch(settingName, "documentFontSize", FALSE, TRUE))
  804.   {
  805.     int n;
  806.     StringToInt(settingValue, &n);
  807.     if (n == 10 || n == 11 || n == 12)
  808.       SetFontSizes(n);
  809.     else
  810.     {
  811.       char buf[200];
  812.       sprintf(buf, "Initialisation file error: nonstandard document font size %d.", n);
  813.       OnInform(buf);
  814.     }
  815.   }
  816.   else
  817.   {
  818.     char buf[200];
  819.     sprintf(buf, "Initialisation file error: unrecognised setting %s.", settingName);
  820.     OnInform(buf);
  821.   }
  822. }
  823.  
  824. Bool ReadCustomMacros(char *filename)
  825. {
  826.   ifstream istr(filename);
  827.   if (istr.bad()) return FALSE;
  828.  
  829.   OnInform("Reading custom macros...");
  830.   CustomMacroList.Clear();
  831.   char ch;
  832.   char macroName[100];
  833.   char macroBody[1000];
  834.   int noArgs;
  835.  
  836.   while (!istr.eof())
  837.   {
  838.     BibEatWhiteSpace(istr);
  839.     istr.get(ch);
  840.     if (istr.eof())
  841.       break;
  842.       
  843.     if (ch != '\\') // Not a macro definition, so must be NAME=VALUE
  844.     {
  845.       char settingName[100];
  846.       settingName[0] = ch;
  847.       BibReadWord(istr, (settingName+1));
  848.       BibEatWhiteSpace(istr);
  849.       istr.get(ch);
  850.       if (ch != '=')
  851.       {
  852.         OnError("Expected = following name: malformed tex2rtf.ini file.");
  853.         return FALSE;
  854.       }
  855.       else
  856.       {
  857.         char settingValue[200];
  858.         BibEatWhiteSpace(istr);
  859.         BibReadToEOL(istr, settingValue);
  860.         RegisterSetting(settingName, settingValue);
  861.       }
  862.     }
  863.     else
  864.     {
  865.       BibReadWord(istr, macroName);
  866.       BibEatWhiteSpace(istr);
  867.       istr.get(ch);
  868.       if (ch != '[')
  869.       {
  870.         OnError("Expected [ followed by number of arguments: malformed tex2rtf.ini file.");
  871.         return FALSE;
  872.       }
  873.       istr >> noArgs;
  874.       istr.get(ch);
  875.       if (ch != ']')
  876.       {
  877.         OnError("Expected ] following number of arguments: malformed tex2rtf.ini file.");
  878.         return FALSE;
  879.       }
  880.       BibEatWhiteSpace(istr);
  881.       istr.get(ch);
  882.       if (ch != '{')
  883.       {
  884.         OnError("Expected { followed by macro body: malformed tex2rtf.ini file.");
  885.         return FALSE;
  886.       }
  887.       CustomMacro *macro = new CustomMacro(macroName, noArgs, NULL);
  888.       BibReadValue(istr, macroBody, FALSE, FALSE); // Don't ignore extra braces
  889.       if (strlen(macroBody) > 0)
  890.         macro->macroBody = copystring(macroBody);
  891.     
  892.       BibEatWhiteSpace(istr);
  893.       CustomMacroList.Append(macroName, macro);
  894.       AddMacroDef(macroName, noArgs);
  895.     }
  896.   }
  897.   return TRUE;
  898. }
  899.  
  900. CustomMacro *FindCustomMacro(char *name)
  901. {
  902.   wxNode *node = CustomMacroList.Find(name);
  903.   if (node)
  904.   {
  905.     CustomMacro *macro = (CustomMacro *)node->Data();
  906.     return macro;
  907.   }
  908.   return NULL;
  909. }
  910.  
  911. // Display custom macros
  912. void ShowCustomMacros(void)
  913. {
  914.   wxNode *node = CustomMacroList.First();
  915.   if (!node)
  916.   {
  917.     OnInform("No custom macros loaded.\n");
  918.     return;
  919.   }
  920.   
  921.   char buf[400];
  922.   while (node)
  923.   {
  924.     CustomMacro *macro = (CustomMacro *)node->Data();
  925.     sprintf(buf, "\\%s[%d]\n    {%s}", macro->macroName, macro->noArgs,
  926.      macro->macroBody ? macro->macroBody : "");
  927.     OnInform(buf);
  928.     node = node->Next();
  929.   }
  930. }
  931.  
  932.